package models

import (
	"errors"
	"fmt"
	"io"
	"os"
	"strings"
	"time"

	"io/ioutil"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
)

// Storage ...
type Storage interface {
	Save(reader io.Reader) error
	Get() ([]byte, error)
	Options(key, value string) Storage
}

// LocalFile ...
type LocalFile struct {
	options map[string]string
}

// Oss ...
type Oss struct {
	options map[string]string
}

// NewStore ...
func NewStore(location string) (store Storage, err error) {
	if strings.HasPrefix(location, "oss:") {
		store = &Oss{options: make(map[string]string)}
		store.Options("endpoint", strings.Replace(location, "oss:", "", -1)).
			Options("akid", "LTAIQEtPo8rFH3xL").
			Options("aks", "EPtQJjJYX8fBww68fstbcs6TMDTSjm")
	} else if strings.HasPrefix(location, "file:") {
		dir := strings.Replace(location, "file:", "", -1)
		lf := &LocalFile{options: make(map[string]string)}
		lf.Options("dir", dir)
		lf.initDir(dir)
		store = lf
	} else {
		err = errors.New("invalid location")
	}
	return
}

// Save to local file
func (l *LocalFile) Save(reader io.Reader) (err error) {
	var f *os.File

	fn := fmt.Sprintf("%s%s", l.options["dir"], l.options["fn"])
	tfn := fmt.Sprintf("%s_%s", fn, time.Now().Format("20060102150405"))

	if f, err = os.Create(tfn); err == nil {
		defer f.Close()
		if _, err = io.Copy(f, reader); err == nil {
			err = os.Rename(tfn, fn)
		}
	}
	return
}

// Get ...
func (l *LocalFile) Get() (p []byte, err error) {
	var file *os.File
	if file, err = os.Open(fmt.Sprintf("%s%s", l.options["dir"], l.options["fn"])); err == nil {
		p, err = ioutil.ReadAll(file)
	}
	return
}

// Options ...
func (l *LocalFile) Options(key, value string) Storage {
	l.options[key] = value
	return l
}

func (l *LocalFile) initDir(dir string) {
	l.createDirIfNotExists(fmt.Sprintf("%sad/", dir))
	l.createDirIfNotExists(fmt.Sprintf("%sauth/", dir))
	l.createDirIfNotExists(fmt.Sprintf("%qrcode/", dir))
}

func (l *LocalFile) createDirIfNotExists(dir string) {
	if _, err := os.Stat(dir); err != nil {
		if os.IsNotExist(err) {
			os.MkdirAll(dir, 0777)
		}
	}
}

// Save to aliyun oss
func (o *Oss) Save(reader io.Reader) (err error) {
	var client *oss.Client
	var bucket *oss.Bucket

	ep := o.options["endpoint"]
	bn := o.options["bucketname"]
	ak := o.options["akid"]
	fn := o.options["fn"]
	aks := o.options["aks"]

	if client, err = oss.New(ep, ak, aks); err == nil {
		if bucket, err = client.Bucket(bn); err == nil {
			err = bucket.PutObject(fn, reader)
		}
	}
	return
}

// Get ...
func (o *Oss) Get() (p []byte, err error) {
	var client *oss.Client
	var bucket *oss.Bucket
	var reader io.ReadCloser

	ep := o.options["endpoint"]
	bn := o.options["bucketname"]
	ak := o.options["akid"]
	fn := o.options["fn"]
	aks := o.options["aks"]

	if client, err = oss.New(ep, ak, aks); err == nil {
		if bucket, err = client.Bucket(bn); err == nil {
			if reader, err = bucket.GetObject(fn); err == nil {
				p, err = ioutil.ReadAll(reader)
			}
		}
	}
	return
}

// Options ...
func (o *Oss) Options(key, value string) Storage {
	o.options[key] = value
	return o
}
